/* $Id: message.cc 20 2011-01-13 11:30:42Z cambei@gmail.com $ */
/* test */
#include "message.h"
#include <iostream>
#include <sstream>
#include <fcntl.h>
#include <netinet/in.h>
#include <assert.h>

namespace sobriety
{
	Message::Message(int typeIn, struct bgp_settings *bgp_in)
	{
		type = typeIn;
		length = 0;

		settings = bgp_in;
	}

	int Message::getLength()
	{
		return length;
	}

	void *Message::getData() //TODO should be renamed to show it gets the full message
	{
		char *full_message = NULL;
		int data_length = 0;
		switch(type)
		{
			case kOpen:
				data_length = makeOpen(NULL);
				break;
			case kKeepalive:
				// DOESN'T NEED TO DO ANYTHING!
				break;
			case kUpdate:
				break;
			case kNotification:
				std::string testing("YOU ARE A STOOPID!");
				data_length = makeNotification(4, 0, testing); // TESTING!!! WRONG PLACE FOR DEFINITION!!
				break;
		}	

		makeHeader(data_length);
		full_message = (char *)calloc((HEADER_SIZE + data_length), 1);
		assert(full_message != NULL);
		memcpy(full_message, header, HEADER_SIZE);
		free(header);
		if(type != kKeepalive)
		{
			memcpy((full_message + HEADER_SIZE), data, data_length);
			free(data);
		}

		return full_message;	
	}

	int Message::makeNotification(int error_code, int error_subcode, std::string description)
	{
		data = (char*)calloc(10, 1);
		assert(data != NULL);
		void *cur_ptr = data;
		int length = 0;

		packData(&cur_ptr, &error_code, 1);
		packData(&cur_ptr, &error_subcode, 1);
		length = 2;

		memcpy(cur_ptr, description.c_str(), description.length());
		length += description.length();
	}

	void Message::makeHeader(int length_in)
	{
		header = (char*)calloc(HEADER_SIZE, 1);
		if (header)
		{
			length = HEADER_SIZE + length_in;
			void *cur_ptr;
			cur_ptr = header;

			char tmpChar[] = {0xFF};
			for(int i = 0; i < 16; i++)
				packData(&cur_ptr, tmpChar, sizeof(tmpChar));

			packData(&cur_ptr, &length, 2);

			packData(&cur_ptr, &type, 1);
		}
		else
		{
			exit(1);
		}
	}

	int Message::makeOpen(struct opt_param *param_list)
	{
		data = (char*)calloc(10, 1);
		assert(data != NULL);
		void *cur_ptr = data;
		int length = 0;

		packData(&cur_ptr, &settings->version, sizeof(settings->version));
		packData(&cur_ptr, &settings->as, sizeof(settings->as));
		assert(settings->hold >= 3);
		packData(&cur_ptr, &settings->hold, sizeof(settings->hold));
		packData(&cur_ptr, &settings->id, sizeof(settings->id));

		if(param_list == NULL)
		{
			// no optional parameters
			uint8_t no_opts = 0;
			packData(&cur_ptr, &no_opts, sizeof(no_opts));
			length = 10;	
		}
		else
		{
			// we have option parameters, handle them
		}
		return length;
	}


	void Message::packData(void **ptr_in, void *data_in, size_t data_size)
	{
		uint16_t tmp16;
		uint32_t tmp32;
		switch(data_size)
		{
			case 1: // 8 bit
				memcpy(*(ptr_in), (uint8_t*)data_in, data_size);
				*(ptr_in) = (uint8_t*)*(ptr_in) + 1;
				break;
			case 2: // 16 bit
				tmp16 = htons(*((uint16_t *)data_in));
				memcpy(*(ptr_in), &tmp16, data_size);
				*(ptr_in) = (uint16_t*)*(ptr_in) + 1;
				break;
			case 4: // 32 bit
				uint32_t tmp32 = htonl(*((uint32_t*)data_in));
				memcpy(*(ptr_in), &tmp32, data_size);
				*(ptr_in) = (uint32_t*)*(ptr_in) + 1;
				break;
		}
	}


	struct opt_param {
		struct opt_param *next;
		uint8_t type;
		uint8_t length;
		char *value;
	};
}
